package net.pingfang.devicelock.util;

import com.alibaba.fastjson.JSONObject;
import iot.codec.sdkdemo.dto.DecodeParamDTO;
import iot.codec.sdkdemo.dto.enCodeDto.EnCode0310ParamDTO;
import iot.codec.sdkdemo.service.impl.CodecServiceImpl;
import lombok.extern.slf4j.Slf4j;
import net.pingfang.devicelock.enums.BufferLimits;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadLocalRandom;

@Slf4j
public class DeviceDecodeUtil {

    /**
     * 电子锁发送报文解析 GPS物理位置类
     * GPSDecode
     */
    public static String TrimDecode(byte[] b){
        DecodeParamDTO paramDTO = new DecodeParamDTO();
        paramDTO.setHex(bytesToHex(b).replaceAll(" ",""));
        log.info(bytesToHex(b).replaceAll(" ","")+"=========");
        return CodecServiceImpl.deCode0200(paramDTO);
    }

    //向电子锁发送施封指令  电子锁施封成功之后会返回响应是否成功、设备号、流水号
    public static String DeviceLock(){
        EnCode0310ParamDTO dto = new EnCode0310ParamDTO();
        dto.setDeviceCode("001342196992");
        dto.setFlowId(5);
        JSONObject jo = new JSONObject();
        JSONObject jo1 = new JSONObject();
        jo1.put("sealType",1); //0解封unseal,1施封seal
        jo1.put("sealUser","PFKJ");
        jo.put("SwitchLockInst",JSONObject.toJSONString(jo1));
        dto.setParameter(jo);
        log.info("enCode0310----(unseal)---->{}", CodecServiceImpl.enCode0310(dto));
        return CodecServiceImpl.enCode0310(dto);
    }

    //终端参数应答Terminal response
    public static String TerminalParamsResponse(byte[] b){
        DecodeParamDTO paramDTO = new DecodeParamDTO();
        paramDTO.setHex(bytesToHex(b).replaceAll(" ",""));
        log.info("deCode0311---->{}", CodecServiceImpl.deCode0311(paramDTO));
        return CodecServiceImpl.deCode0311(paramDTO);
    }

    public static String Lock_Device(String code,int seal){
        EnCode0310ParamDTO dto = new EnCode0310ParamDTO();
        dto.setDeviceCode(code);
        //随机数
        dto.setFlowId(ThreadLocalRandom.current().nextInt(0, 65535));
        JSONObject jo = new JSONObject();
        JSONObject jo1 = new JSONObject();
        jo1.put("sealType",seal); //0解封unseal,1施封seal
        jo1.put("sealUser","PFKJ");
        jo.put("SwitchLockInst",JSONObject.toJSONString(jo1));
        //JSONObject
        dto.setParameter(jo);
        log.info("enCode0310----(unseal)---->{}", CodecServiceImpl.enCode0310(dto));
        return CodecServiceImpl.enCode0310(dto);
    }

    public static String bytesToHex(byte[] bytes, int start, int end) {
        StringBuilder hexStr = new StringBuilder();
        for(int i = start; i < end ;i ++){
            hexStr.append(String.format("%02X ", bytes[i]));
        }
        return hexStr.toString().replaceAll(" ","");
    }

    public static String bytesToHex(byte[] bytes) {
        StringBuilder hexStr = new StringBuilder();
        for(byte b : bytes){
            hexStr.append(String.format("%02X ", b));
        }
        return hexStr.toString().replaceAll(" ","");
    }


//    private static final ReentrantLock lock = new ReentrantLock();

    /**
     * 检验锁ID
     * 位掩码操作
     * 提取lockId数组第8个元素(索引7)的低4为二进制值
     */
    public static boolean CheckLockId(byte[] lockId){
        //参数校验
        if(lockId == null || lockId.length < 8){
            return false;
        }
        int checkNum = lockId[7] & 0xFF;
        int equalValue ,addValue;
        int powerSum = 0;
        int powerNum = 0;
        int lowBit,highBit;
        for(int i = 0 ; i < 8;i++){
            if(i == 7){
                //第8字节特殊处理
                highBit = (lockId[i] >> 4);
                //powerNum是整数时 使用更好highBit * (1L << powerNum)
                powerSum += (int) (highBit * Math.pow(2, powerNum));
                powerNum++;
            }else if(lockId[i] >= 'A' && lockId[i] <= 'Z'){
                //字母处理
                addValue = ((lockId[i] - 'A' + 10)/11);
                equalValue = lockId[i] - 'A' + 10 + addValue;
                powerSum += (int) (equalValue * Math.pow(2,powerNum));
                powerNum ++;
            }else{
                //普通字节分解高低4位
                highBit =  lockId[i] >> 4;//获取字节的高4位
                lowBit = lockId[i] & 0x0F;//获取字节的低4位
                powerSum += (int) (highBit * Math.pow(2,powerNum));
                powerNum ++;
                powerSum += (int) (lowBit * Math.pow(2, powerNum));
                powerNum ++;
            }
        }
        int remainder = powerSum % 11;
        log.info("remainder:{}",remainder);
        remainder = (remainder == 10) ? 0 : remainder;//余数转0
        //元cpp方法中存在byte[]checkLockId参数
//        lock.lock();
//        try{
//            //若需写回校验值
//            if(checkLockId != null && checkLockId.length >= 8){
//                //可以使用ForkJoinPool  大数据并行优化
//                System.arraycopy(lockId,0, checkLockId,0,8);
//                checkLockId[7] = (byte) ((checkLockId[7] & 0xF0) | remainder);
//            }
//        }finally {
//            lock.unlock();
//        }
        log.info("========={}",(remainder == checkNum));
        //最终检验
        return (remainder == checkNum);

    }

    /**
     * 给阅读器发送施封报文
     * 锁名称，操作口令
     * 开始向阅读器发送操作报文
     *
     */
    public static byte[] LockCmd(String lockNum,String passWord,String nowTime){

        if(lockNum == null || lockNum.length() < 8){
            return null;
        }
        byte[] cmd = new byte[BufferLimits.MAX_CMDBUFF_LEN.getValue()];
        int cmdflag = 0;
        cmd[cmdflag++] = 0x7B;
        cmd[cmdflag++] = 0x5A;
        byte[] lockId = new byte[BufferLimits.LOCK_ID_LEN.getValue()];
        boolean bRet = GetLockIdByLockNum(lockNum,lockId);

        if(bRet){
            bRet = CheckLockId(lockId);
            log.info("==========lockNum:{},password:{}，\r\n bRet{}",lockNum,passWord,bRet);
            if(!bRet){
                return null;
            }
        }
        memcpy(cmd,lockId,cmdflag,8);
        cmdflag += 8;
        cmd[cmdflag ++] = 0x41;
        cmd[cmdflag ++] = 0x12;
        memcpy(cmd,passWord.getBytes(),cmdflag,10);
        cmdflag += 10;
        //获取当前时间并转化为byte[]
        byte[] bcdTime = new byte[BufferLimits.BCD_TIME_LEN.getValue()];
        byte[] ChTime = nowTime.getBytes();
        Str2Hex(ChTime,bcdTime);
        bcdTime[7] = BCDCrcCheck(bcdTime);
        memcpy(cmd,bcdTime,cmdflag,8);
        cmdflag += 8;

        //数组越界，Len = 64?
        Crc16Ccitt(cmd,cmdflag );
        /*
        * 从缓冲区末尾向前遍历，虚招第一个非空字节的位置
        * 若找到非空字节，记录其索引i并将有效的长度设为i+1(索引从0开始)
        * 若全为空自己诶，len保持0
        * */
        int cmdLen = GetCmdLen(cmd);
        log.info("cmLen:{}",cmdLen);
        return cmd;
    }

    /**
     * 时间校验
     * 异或校验函数，用于对数据进行校验和计算
     *
     */
    private static byte BCDCrcCheck(byte[] data) {
        if (data == null || data.length == 0) {
            log.error("错误异常，传入时间不对");
        }
        // 自动修正checkLen不超过data长度
        int actualLen = 0;
        if (data != null) {
            actualLen = Math.min(7, data.length);
        }
        byte resultCrc = 0; //取第一个字节作为初始值
        if (data != null) {
            resultCrc = data[0];
        }

        for (int i = 1; i < actualLen; i++) {
            //按位异或(XOR)并赋值
            resultCrc = (byte)(resultCrc ^ data[i]);
        }
        // 转换为无符号int（避免Java byte的符号问题）
        return resultCrc;
    }

    private static void memcpy(byte[] cmd,byte[] bytes,int start,int len){
        for(int i = 0 ; i < len; i++ ){
            cmd[start ++] = bytes[i];
        }
    }

    /**
     * 根据锁编号获取锁ID 转换锁ID为数组 CNHD1342196992
     * @param lockNum 输入参数，锁编号字符串
     * @return 成功返回true，失败返回false
     */
    private static boolean GetLockIdByLockNum( String lockNum,byte[] lockId){
        if(lockId == null || lockNum == null){
            return false;
        }
        try{
            byte[] bytes = lockNum.getBytes();
            for(int i = 0 ; i < 4; i++){
                lockId[i] = bytes[i];
            }
            //提取子串并转化为无符号整数
            long id = Long.parseUnsignedLong(lockNum.substring(4));
//        lockId[4] = (byte)(ir >>>24);
//        lockId[5] = (byte)(ir >>>16);
//        lockId[6] = (byte)(ir >>>8);
//        lockId[7] = (byte)ir;
//            lockId[4] = (byte)id;
//            lockId[5] = (byte)(id >>>8);
//            lockId[6] = (byte)(id >>>16);
//            lockId[7] = (byte)(id >>>24);
            //转化为字节数组(小端序)
            ByteBuffer buffer = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN)
                    .putInt((int)id);
            byte[] idch = buffer.array();
            //逆序填充到lockId[4]~lockId[7](小端转大端)
            lockId[4] = idch[3]; //最高有效字节
            lockId[5] = idch[2];
            lockId[6] = idch[1];
            lockId[7] = idch[0]; //最低有效字节
        }catch (Exception e){
            log.error("转换锁id异常:{}",e.getMessage());
            return false;
        }
        return true;
    }

    private static final ThreadLocal<SimpleDateFormat> threadLocalSdf =
            ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyyMMddHHmmss"));

    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(4);
        executor.submit(() -> {
            String formattedTime = threadLocalSdf.get().format(new Date());
            log.info("当前时间:{} ;byte:{},长度:{}" , formattedTime,
                    Arrays.toString(formattedTime.getBytes())
                    ,formattedTime.getBytes().length);
        });
        executor.shutdown();
    }

    /**
     * 将一个字符串作为十六进制串转化为一个字节数组，字节间可用空格分隔，
     * 返回转换后的字节数组长度，同时字节数组长度自动设置。
     *
     * @param strSrc 当前时间 YYYYMMDDHHmmss
     */
    private static int Str2Hex(byte[] strSrc, byte[] chars){
        int t,t1;
        int rlen = 0;
        for(int i = 0 ; i < strSrc.length;){
            byte l,h = strSrc[i];
            if(h == ' '){
                i ++;
                continue;
            }
            i++;
            if( i >= strSrc.length ){
                break;
            }
            l = strSrc[i];
            t = HexChar(h);
            t1 = HexChar(l);
            if(( t == 16) || (t1 == 16))
                break;
            else
                t = t * 16 + t1;
            i++;
            chars[rlen] = (byte)t;
            rlen ++;
        }
        return rlen;
    }

    //转换为16进制字符
    private static int HexChar(byte l) {
        if( (l >= '0' ) && (l <= '9'))
            return l - 0x30;
        else if((l >= 'A') && (l <= 'F'))
            return l - 'A' + 10;
        else if((l >='A') && (l <= 'f'))
            return l - 'a' + 10;
        else
            return 0x10;
    }
    //报文校验
    private static void Crc16Ccitt(byte[] data,int Len){
        int crc = 0xFFFF;
        int polynomial = 0x1021;
        for(int i = 0 ; i < Len;i++){
            for(int j = 0 ; j < 8; j++){
                boolean bit = ((data[i] >>  (7-j) & 1) == 1);
                boolean c15 = ((crc >> 15 & 1) == 1);
                crc <<= 1;
                if(c15 ^ bit){
                    crc ^= polynomial;
                }
            }

        }
        crc &= 0xFFFF;
        data[Len] = (byte) (crc >> 8);
        data[Len++] = (byte) (crc & 0x00FF);
    }

    //计算需要发生的长度，将空白值去除
    private static int GetCmdLen(byte[] data){
        int bufLen = data.length;
        if (bufLen == 0)
        {
            return 0;
        }
        int len = 0;
        for (int i = bufLen - 1; i >= 0; i--)
        {
            if ((byte)(data[i] + i) != 0x00)
            {
                len = i + 1;
                break;
            }
        }
        return len;
    }

    public void processData(byte[] recvBuf){
        if(recvBuf != null && recvBuf.length > 0){
            int len = recvBuf.length;
            byte[] onePack = new byte[1024];
        }
    }

    public String RecvOnePack(String  Buf){
        return null;

    }

}
